Improve local voice readiness and add Composio trigger history#516
Improve local voice readiness and add Composio trigger history#516senamakel merged 7 commits intotinyhumansai:mainfrom
Conversation
- Updated the Home component to improve readiness checks for local AI assets, ensuring a more robust UI experience. - Refactored the LocalAiService to provide clearer state management for STT and TTS models, including handling on-demand downloads and error logging. - Enhanced the voice status function to accurately reflect the availability of transcription backends, improving overall system reliability. - Introduced warnings for on-demand model downloads to inform users about potential delays in functionality.
- Added a polling mechanism to periodically check the voice server status every 2 seconds, ensuring the overlay remains in sync with the server state. - Introduced logic to activate or dismiss the speech-to-text (STT) mode based on the server's recording or transcribing state, enhancing user experience and responsiveness. - Utilized the existing `callCoreRpc` function to fetch server status, improving reliability in state management during brief disconnects or reconnections.
- Updated the prompt construction logic in `LocalAiService` to enhance clarity and functionality. - When `no_think` is set, the system prompt now includes a directive for the model to respond with only the final answer, improving response accuracy. - Refactored the prompt and system parameters to ensure they are correctly formatted and passed to the `OllamaGenerateRequest`, enhancing overall request handling.
- Updated the STT readiness logic in `VoicePanel` to account for both 'ready' and 'ondemand' states, improving the accuracy of readiness assessments. - Refined the `useVoiceSkillStatus` hook to ensure it only blocks when the local AI's STT state is explicitly 'missing', enhancing overall system reliability and user experience.
- Added `ComposeioTriggerHistory` component to display a list of ComposeIO trigger events with formatted timestamps and payloads. - Introduced `useComposeioTriggerHistory` hook to manage fetching and state of trigger history entries, including error handling and loading states. - Updated `Webhooks` page to integrate the new component and hook, replacing previous webhook activity display with ComposeIO trigger history. - Created utility functions for formatting timestamps and payloads for better readability in the UI. - Established backend support for fetching trigger history through new Tauri commands, ensuring robust data handling and storage.
📝 WalkthroughWalkthroughThis PR adds a persistent ComposeIO trigger history (backend storage, RPC, and frontend UI with polling), relaxes STT readiness gating to accept Changes
Sequence Diagram(s)sequenceDiagram
participant ReactApp as React App<br/>(Webhooks)
participant Hook as useComposeioTriggerHistory<br/>Hook
participant RPC as Core RPC Client
participant Core as Core Service
participant FS as Workspace<br/>Filesystem
Note over ReactApp,FS: Initial Load & Polling (5s interval)
ReactApp->>Hook: mount component
Hook->>Hook: initialize state (loading=true)
Hook->>RPC: openhumanComposioListTriggerHistory(limit)
RPC->>Core: openhuman.composio_list_trigger_history
Core->>FS: scan triggers/*.jsonl
Core->>FS: read & parse JSON lines
FS-->>Core: entries (newest-first)
Core-->>RPC: ComposioTriggerHistoryResult
RPC-->>Hook: result.entries[]
Hook->>Hook: setState(entries, archiveDir, currentDayFile)
Hook-->>ReactApp: trigger history + archive info
ReactApp->>ReactApp: render ComposeioTriggerHistory
Note over Hook,FS: Polling continues every 5s
Hook->>RPC: openhumanComposioListTriggerHistory()
RPC->>Core: openhuman.composio_list_trigger_history
Core->>FS: re-scan for new entries
FS-->>Core: updated entries
Core-->>Hook: latest history
Hook->>Hook: setState(loading=false)
Hook-->>ReactApp: re-render with new entries
sequenceDiagram
participant Overlay as OverlayApp<br/>Component
participant RPC as Core RPC Client
participant Core as Core Voice<br/>Service
participant UI as Overlay UI
Note over Overlay,UI: Polling Loop (2s interval)
Overlay->>Overlay: useEffect (on mount)
Overlay->>Overlay: startInterval (2s)
loop Every 2 seconds
Overlay->>RPC: callCoreRpc<br/>voice_server_status
RPC->>Core: openhuman.voice_server_status
Core-->>RPC: {state: recording|transcribing|idle|stopped}
RPC-->>Overlay: voice status
alt state=recording/transcribing && mode!=stt
Overlay->>Overlay: setMode('stt')
Overlay->>Overlay: setBubble(listening|transcribing)
Overlay->>UI: update overlay message
else state=idle/stopped && mode=stt
Overlay->>Overlay: goIdle()
Overlay->>UI: dismiss overlay
end
end
Note over Overlay: cleanup on unmount
Overlay->>Overlay: clearInterval()
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (5)
src/openhuman/local_ai/service/public_infer.rs (1)
312-319: Add a debug checkpoint for theno_thinkbranch decision.This changed control path is important for inference behavior, but it currently has no branch-level diagnostic log (e.g.,
no_think,max_tokens,allow_empty, model id) with a stable prefix like[local_ai:infer].♻️ Suggested patch
let started = std::time::Instant::now(); + tracing::debug!( + no_think, + allow_empty, + temperature, + max_tokens = ?max_tokens, + model = %model_ids::effective_chat_model_id(config), + "[local_ai:infer] building generate request" + ); + // When `no_think` is set, append the instruction to the system // prompt so the model treats it as a directive rather than content // it might parrot back. let effective_system = if no_think {As per coding guidelines,
src/**/*.rs: “Add substantial, development-oriented logs on new/changed flows; include logs at entry/exit points, branch decisions, external calls, retries/timeouts, state transitions, and error handling paths.”🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/openhuman/local_ai/service/public_infer.rs` around lines 312 - 319, Add a debug checkpoint when deciding the `no_think` branch: where `effective_system` is set (using the `no_think` flag and the `effective_system` variable), emit a development-level log with the stable prefix "[local_ai:infer]" that records the branch decision and key inference parameters (at minimum `no_think`, `max_tokens`, `allow_empty`, and the model id/identifier used). Place the log right at the branch (both when the `no_think` true path is taken and the else path) so entry into that control path is visible in logs; use the existing logging/tracing facility used elsewhere in this module for consistency.app/src/components/settings/panels/VoicePanel.tsx (1)
68-70: Add a namespaced debug checkpoint for STT readiness.Line 68–70 changed the readiness gate, but there’s no trace for
sttAssetStatevsvoiceResponse.stt_available. Add a dev-oriented, grep-friendly debug log at this decision point to simplify field diagnosis.As per coding guidelines: "Add substantial, development-oriented logs on new/changed flows in TypeScript/React app code; use namespaced debug logs and dev-only detail as needed."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/components/settings/panels/VoicePanel.tsx` around lines 68 - 70, Add a development-only, namespaced debug checkpoint at the STT readiness decision in VoicePanel: capture and log sttAssetState, sttAssetOk, and voiceResponse.stt_available before calling setSttReady so you can grep for the log (e.g. "VoicePanel:stt"). Wrap the log in a dev-only guard (process.env.NODE_ENV !== 'production') and use console.debug or the app logger to emit a single, clear message showing the values used to compute readiness (referencing sttAssetState, sttAssetOk, voiceResponse, assetsResponse, and setSttReady).app/src/utils/tauriCommands/composio.ts (1)
2-2: SplitCommandResponseinto a type-only import.
CommandResponseis only referenced in type positions here, so it should come fromimport typerather than the value import.🛠️ Suggested change
-import { CommandResponse, isTauri } from './common'; +import type { CommandResponse } from './common'; +import { isTauri } from './common';As per coding guidelines "Use
import typefor TypeScript type-only imports where appropriate".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/utils/tauriCommands/composio.ts` at line 2, The import currently brings CommandResponse as a value; change it to a type-only import so TypeScript knows it’s type-only: replace the single import of CommandResponse and isTauri with a type import for CommandResponse and a normal import for isTauri (i.e., import type { CommandResponse } ... and import { isTauri } ...), updating the import line in composio.ts where CommandResponse and isTauri are referenced.app/src/overlay/OverlayApp.tsx (1)
378-380: Log polling failures at debug level instead of silently dropping them.Right now the new fallback path has no trace when RPC polling stops working, which makes stuck-overlay reports much harder to diagnose. A throttled
[overlay]console.debughere would be enough.As per coding guidelines "Add substantial, development-oriented logs on new/changed flows in TypeScript/React app code; use namespaced debug logs and dev-only detail as needed".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/overlay/OverlayApp.tsx` around lines 378 - 380, The catch block in OverlayApp.tsx that swallows polling errors should log them at debug level so RPC polling failures are observable; update the catch in the OverlayApp component's polling routine to call console.debug('[overlay] RPC poll failed', err) (include the caught error variable) and guard it with a dev-only check (e.g. if (process.env.NODE_ENV !== 'production')) and a simple throttle (module-level lastDebugTs timestamp, only log if now - lastDebugTs > 5000ms) so messages are emitted at most once every few seconds.src/openhuman/composio/trigger_history.rs (1)
19-26: HandleOnceLock::setresult explicitly instead of silently discarding it.Ignoring
GLOBAL_TRIGGER_HISTORY.set(store)hides initialization races and makes path mismatches hard to diagnose.🔧 Proposed fix
- let store = Arc::new(ComposioTriggerHistoryStore::new(&workspace_dir)?); - let _ = GLOBAL_TRIGGER_HISTORY.set(store); + let store = Arc::new(ComposioTriggerHistoryStore::new(&workspace_dir)?); + if GLOBAL_TRIGGER_HISTORY.set(store).is_err() { + tracing::debug!( + workspace_dir = %workspace_dir.display(), + "[composio][history] global store already initialized" + ); + } Ok(()) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/openhuman/composio/trigger_history.rs` around lines 19 - 26, init_global currently discards the result of GLOBAL_TRIGGER_HISTORY.set(store), hiding races and path mismatches; update init_global to check the Result returned by GLOBAL_TRIGGER_HISTORY.set(store) and handle the Err case explicitly (e.g., return Err with a message that includes the workspace_dir and/or the created ComposioTriggerHistoryStore context) so callers see initialization failures; reference the GLOBAL_TRIGGER_HISTORY OnceLock, the init_global function, and the ComposioTriggerHistoryStore::new(workspace_dir) call when adding this explicit error handling and propagation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/src/components/webhooks/ComposeioTriggerHistory.tsx`:
- Around line 18-23: The formatPayload function may return undefined because
JSON.stringify can return undefined for values like undefined, functions or
Symbols; update formatPayload to coalesce the JSON.stringify result to a string
(e.g., store the result of JSON.stringify(payload, null, 2) in a variable and
return that value if non-null/undefined, otherwise return String(payload)),
ensuring the function always returns a string and preserving the existing
try/catch behavior.
In `@app/src/hooks/useComposeioTriggerHistory.ts`:
- Around line 54-57: The hook early-returns when snapshot.sessionToken is falsy
but leaves previous cached state (entries, archiveDir, currentDayFile) intact;
update the useComposeioTriggerHistory hook so that whenever
snapshot.sessionToken is falsy you explicitly clear/reset entries, archiveDir,
and currentDayFile to their initial empty values (e.g., []/''/null) before
returning; apply the same clearing logic in the other effect that checks
snapshot.sessionToken so no stale/sensitive trigger history remains after logout
or token expiry.
- Around line 32-52: The refresh function in useComposeioTriggerHistory can run
concurrently because setInterval may call it while a prior call is still in
flight; add an in-flight guard (e.g., a useRef<boolean> isRefreshing or an
AbortController) around refresh so it immediately returns if already running,
set the guard true at start and false in finally, and ensure the interval
callback uses that guarded refresh (and clears the interval on unmount); update
references to refresh, setInterval usage, and any cleanup logic so overlapping
RPCs cannot occur.
In `@app/src/overlay/OverlayApp.tsx`:
- Around line 356-370: Change the recovery condition so the overlay switches to
STT whenever the server is recording/transcribing and the overlay is not already
in 'stt' (replace currentMode === 'idle' with currentMode !== 'stt'), and before
calling setMode('stt')/setBubble(...) clear any pending attention-dismiss timer
(e.g., clearTimeout(attentionDismissTimeout) or equivalent) so an existing
attention bubble/timer cannot prevent the UI from updating; apply the same
change to the other identical branch around line 389.
In `@src/openhuman/about_app/catalog.rs`:
- Around line 579-580: The capability description string assigned to description
in the catalog entry mistakenly uses "ComposeIO"; update that literal to use the
consistent product name "Composio" so it matches RPC/domain and other
docs—locate the description field in src/openhuman/about_app/catalog.rs (the
description variable/string) and replace "ComposeIO" with "Composio".
In `@src/openhuman/composio/bus.rs`:
- Around line 169-186: The call to store.record_trigger(...) inside the
EventHandler is performing synchronous file I/O (OpenOptions::open/writeln!) and
must be moved off the Tokio runtime; wrap the work in
tokio::task::spawn_blocking so the blocking record logic runs on a blocking
thread (e.g., call tokio::task::spawn_blocking(move ||
store.record_trigger(...)) and .await the JoinHandle), capture and log any
returned Err from the join or from record_trigger, and keep the surrounding
logic that checks trigger_history::global() and calls
tracing::warn/tracing::debug unchanged except now handling asynchronous spawn
results.
In `@src/openhuman/composio/ops.rs`:
- Around line 205-225: The logs currently include absolute filesystem paths
(config.workspace_dir via tracing::debug! and archive_dir included in the
RpcOutcome message), which may leak PII; update the code that calls
tracing::debug! and RpcOutcome::new in this flow to omit or redact full paths:
remove printing config.workspace_dir.display() from the tracing::debug! call (or
replace with a non-sensitive identifier such as workspace name or basename), and
replace archive_dir in the RpcOutcome message with a redacted or non-path
summary (e.g., "archive present" or a basename/UUID), referencing the
tracing::debug! invocation, the config.workspace_dir usage, the archive_dir
variable and the RpcOutcome::new call so you edit the exact spots.
In `@src/openhuman/composio/trigger_history.rs`:
- Around line 76-92: The record_trigger function currently opens the archive
file and calls writeln! directly, which can interleave under concurrent
processes; wrap the append with an advisory file lock using fs2::FileExt to
serialize writes: after opening the file (the file variable in record_trigger)
call file.lock_exclusive().map_err(...) before writeln!(file, "{line}") and call
file.unlock().map_err(...) (or ensure unlock in a drop guard) after the write
and flush; add the fs2 dependency and necessary use statement and propagate
errors similarly to the existing map_err messages referencing path.display().
---
Nitpick comments:
In `@app/src/components/settings/panels/VoicePanel.tsx`:
- Around line 68-70: Add a development-only, namespaced debug checkpoint at the
STT readiness decision in VoicePanel: capture and log sttAssetState, sttAssetOk,
and voiceResponse.stt_available before calling setSttReady so you can grep for
the log (e.g. "VoicePanel:stt"). Wrap the log in a dev-only guard
(process.env.NODE_ENV !== 'production') and use console.debug or the app logger
to emit a single, clear message showing the values used to compute readiness
(referencing sttAssetState, sttAssetOk, voiceResponse, assetsResponse, and
setSttReady).
In `@app/src/overlay/OverlayApp.tsx`:
- Around line 378-380: The catch block in OverlayApp.tsx that swallows polling
errors should log them at debug level so RPC polling failures are observable;
update the catch in the OverlayApp component's polling routine to call
console.debug('[overlay] RPC poll failed', err) (include the caught error
variable) and guard it with a dev-only check (e.g. if (process.env.NODE_ENV !==
'production')) and a simple throttle (module-level lastDebugTs timestamp, only
log if now - lastDebugTs > 5000ms) so messages are emitted at most once every
few seconds.
In `@app/src/utils/tauriCommands/composio.ts`:
- Line 2: The import currently brings CommandResponse as a value; change it to a
type-only import so TypeScript knows it’s type-only: replace the single import
of CommandResponse and isTauri with a type import for CommandResponse and a
normal import for isTauri (i.e., import type { CommandResponse } ... and import
{ isTauri } ...), updating the import line in composio.ts where CommandResponse
and isTauri are referenced.
In `@src/openhuman/composio/trigger_history.rs`:
- Around line 19-26: init_global currently discards the result of
GLOBAL_TRIGGER_HISTORY.set(store), hiding races and path mismatches; update
init_global to check the Result returned by GLOBAL_TRIGGER_HISTORY.set(store)
and handle the Err case explicitly (e.g., return Err with a message that
includes the workspace_dir and/or the created ComposioTriggerHistoryStore
context) so callers see initialization failures; reference the
GLOBAL_TRIGGER_HISTORY OnceLock, the init_global function, and the
ComposioTriggerHistoryStore::new(workspace_dir) call when adding this explicit
error handling and propagation.
In `@src/openhuman/local_ai/service/public_infer.rs`:
- Around line 312-319: Add a debug checkpoint when deciding the `no_think`
branch: where `effective_system` is set (using the `no_think` flag and the
`effective_system` variable), emit a development-level log with the stable
prefix "[local_ai:infer]" that records the branch decision and key inference
parameters (at minimum `no_think`, `max_tokens`, `allow_empty`, and the model
id/identifier used). Place the log right at the branch (both when the `no_think`
true path is taken and the else path) so entry into that control path is visible
in logs; use the existing logging/tracing facility used elsewhere in this module
for consistency.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 175e7fd0-dd45-4a85-825e-db2bd747f8fd
📒 Files selected for processing (20)
app/src/components/settings/panels/VoicePanel.tsxapp/src/components/webhooks/ComposeioTriggerHistory.tsxapp/src/features/voice/useVoiceSkillStatus.tsapp/src/hooks/useComposeioTriggerHistory.tsapp/src/overlay/OverlayApp.tsxapp/src/pages/Home.tsxapp/src/pages/Webhooks.tsxapp/src/utils/tauriCommands/composio.tsapp/src/utils/tauriCommands/index.tssrc/core/jsonrpc.rssrc/openhuman/about_app/catalog.rssrc/openhuman/composio/bus.rssrc/openhuman/composio/mod.rssrc/openhuman/composio/ops.rssrc/openhuman/composio/schemas.rssrc/openhuman/composio/trigger_history.rssrc/openhuman/composio/types.rssrc/openhuman/local_ai/service/assets.rssrc/openhuman/local_ai/service/public_infer.rssrc/openhuman/voice/ops.rs
- Introduced the `fs2` crate to manage file locking, improving the reliability of file operations in the ComposeIO trigger history. - Updated `ComposioTriggerHistoryStore` to utilize exclusive file locks during archive writing, ensuring data integrity. - Enhanced error handling for file operations, providing clearer logging for failures related to file access and locking. - Refactored the initialization logic for global trigger history to prevent duplicate setups, improving overall stability.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
app/src/overlay/OverlayApp.tsx (1)
339-396: Serializevoice_server_statuspolling.
setInterval(() => void poll(), 2000)can queue another RPC before the previous one settles. When the core is slow or unavailable, this fallback will stack overlappingvoice_server_statuscalls and duplicate state corrections/logs. Gate the loop with aninFlightflag or switch to recursivesetTimeoutso only one poll runs at a time.Suggested fix
useEffect(() => { let disposed = false; + let inFlight = false; const poll = async () => { + if (inFlight) return; + inFlight = true; try { const res = await callCoreRpc<{ state: string; hotkey: string; activation_mode: string; transcription_count: number; last_error: string | null; }>({ method: 'openhuman.voice_server_status' }); if (disposed) return; const serverState = res.state; // 'stopped' | 'idle' | 'recording' | 'transcribing' const currentMode = modeRef.current; ... } catch (err) { if (process.env.NODE_ENV !== 'production') { const now = Date.now(); if (now - lastPollDebugTs > 5000) { lastPollDebugTs = now; console.debug('[overlay] RPC poll failed', err); } } + } finally { + inFlight = false; } };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/overlay/OverlayApp.tsx` around lines 339 - 396, The poll loop in useEffect can start overlapping RPCs because setInterval calls poll every 2s regardless of previous completion; modify the poll logic in OverlayApp.tsx (the poll async function invoked by setInterval) to serialize calls by adding an inFlight boolean (or use a recursive setTimeout) so a new poll returns immediately if inFlight is true; ensure you set inFlight = true at start and false in finally, preserving early returns (e.g., disposed) and still performing the same state updates via setMode, setBubble, clearDismissTimer, and goIdle when appropriate.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/src/overlay/OverlayApp.tsx`:
- Around line 375-379: The poll branch that forces goIdle() when serverState is
'idle'|'stopped' should not cancel an intentional STT linger; update the block
that checks (serverState === 'idle' || serverState === 'stopped') && currentMode
=== 'stt' to only call goIdle() when there is no pending STT dismiss timer/flag
(the timer set by dictation:released/final transcription using
STT_RELEASE_LINGER_MS), e.g. check the existing sttDismissTimerId or
sttDismissPending flag (or add one if missing) and skip the forced dismiss while
that timer/flag is active so the scheduled STT linger can complete.
---
Nitpick comments:
In `@app/src/overlay/OverlayApp.tsx`:
- Around line 339-396: The poll loop in useEffect can start overlapping RPCs
because setInterval calls poll every 2s regardless of previous completion;
modify the poll logic in OverlayApp.tsx (the poll async function invoked by
setInterval) to serialize calls by adding an inFlight boolean (or use a
recursive setTimeout) so a new poll returns immediately if inFlight is true;
ensure you set inFlight = true at start and false in finally, preserving early
returns (e.g., disposed) and still performing the same state updates via
setMode, setBubble, clearDismissTimer, and goIdle when appropriate.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e96ca329-8171-4f35-9be2-9705f15d65c7
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (11)
Cargo.tomlapp/src/components/settings/panels/VoicePanel.tsxapp/src/components/webhooks/ComposeioTriggerHistory.tsxapp/src/hooks/useComposeioTriggerHistory.tsapp/src/overlay/OverlayApp.tsxapp/src/utils/tauriCommands/composio.tssrc/openhuman/about_app/catalog.rssrc/openhuman/composio/bus.rssrc/openhuman/composio/ops.rssrc/openhuman/composio/trigger_history.rssrc/openhuman/local_ai/service/public_infer.rs
✅ Files skipped from review due to trivial changes (5)
- Cargo.toml
- src/openhuman/about_app/catalog.rs
- app/src/components/settings/panels/VoicePanel.tsx
- src/openhuman/local_ai/service/public_infer.rs
- app/src/utils/tauriCommands/composio.ts
🚧 Files skipped from review as they are similar to previous changes (5)
- app/src/components/webhooks/ComposeioTriggerHistory.tsx
- src/openhuman/composio/ops.rs
- src/openhuman/composio/bus.rs
- app/src/hooks/useComposeioTriggerHistory.ts
- src/openhuman/composio/trigger_history.rs
| // Server is idle/stopped but overlay thinks it's in stt → dismiss | ||
| if ((serverState === 'idle' || serverState === 'stopped') && currentMode === 'stt') { | ||
| console.debug(`[overlay] poll sync: server=${serverState}, overlay=stt → dismissing`); | ||
| goIdle(); | ||
| } |
There was a problem hiding this comment.
Don't let the fallback poll cancel the intentional STT linger.
dictation:released and final transcription both schedule STT_RELEASE_LINGER_MS, but this branch calls goIdle() immediately on the next idle/stopped poll. If the poll lands during that window, the final STT bubble disappears early and the linger becomes timing-dependent. Skip the forced dismiss while an STT dismiss timer is already pending.
Suggested fix
- if ((serverState === 'idle' || serverState === 'stopped') && currentMode === 'stt') {
+ if (
+ (serverState === 'idle' || serverState === 'stopped') &&
+ currentMode === 'stt' &&
+ dismissTimerRef.current === null
+ ) {
console.debug(`[overlay] poll sync: server=${serverState}, overlay=stt → dismissing`);
goIdle();
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/src/overlay/OverlayApp.tsx` around lines 375 - 379, The poll branch that
forces goIdle() when serverState is 'idle'|'stopped' should not cancel an
intentional STT linger; update the block that checks (serverState === 'idle' ||
serverState === 'stopped') && currentMode === 'stt' to only call goIdle() when
there is no pending STT dismiss timer/flag (the timer set by
dictation:released/final transcription using STT_RELEASE_LINGER_MS), e.g. check
the existing sttDismissTimerId or sttDismissPending flag (or add one if missing)
and skip the forced dismiss while that timer/flag is active so the scheduled STT
linger can complete.
Summary
Problem
Solution
composio_list_trigger_historypath in the Rust core, then exposed it through the existing controller/JSON-RPC surface.Submission Checklist
app/) and/orcargo test(core) for logic you add or changeapp/test/e2e, mock backend,tests/json_rpc_e2e.rsas appropriate)//////!(Rust), JSDoc or brief file/module headers (TS) on public APIs and non-obvious modulesImpact
Related
Summary by CodeRabbit
New Features
New Features
Improvements